(function($) {
	var methods = {
		initTree : function(options) {
			var settings = $.extend({}, this.treegrid.defaults, options);
			return this.each(function() {
				var $this = $(this);
				$this.treegrid("setTreeContainer", $(this));
				$this.treegrid("setSettings", settings);
				settings.getRootNodes.apply(this, [ $(this) ]).treegrid(
						"initNode", settings)
			})
		},
		initNode : function(settings) {
			return this.each(function() {
				var $this = $(this);
				$this.treegrid("setTreeContainer",
						settings.getTreeGridContainer.apply(this));
				$this.treegrid("getChildNodes").treegrid("initNode", settings);
				$this.treegrid("initExpander").treegrid("initIndent").treegrid(
						"initEvents").treegrid("initState").treegrid(
						"initSettingsEvents")
			})
		},
		initEvents : function() {
			var $this = $(this);
			$this.on("change", function() {
				var $this = $(this);
				$this.treegrid("render");
				if ($this.treegrid("getSetting", "saveState")) {
					$this.treegrid("saveState")
				}
			});
			$this.on("collapse", function() {
				var $this = $(this);
				$this.removeClass("treegrid-expanded");
				$this.addClass("treegrid-collapsed")
			});
			$this.on("expand", function() {
				var $this = $(this);
				$this.removeClass("treegrid-collapsed");
				$this.addClass("treegrid-expanded")
			});
			return $this
		},
		initSettingsEvents : function() {
			var $this = $(this);
			$this
					.on("change",
							function() {
								var $this = $(this);
								if (typeof $this.treegrid("getSetting",
										"onChange") === "function") {
									$this.treegrid("getSetting", "onChange")
											.apply($this)
								}
							});
			$this
					.on("collapse",
							function() {
								var $this = $(this);
								if (typeof $this.treegrid("getSetting",
										"onCollapse") === "function") {
									$this.treegrid("getSetting", "onCollapse")
											.apply($this)
								}
							});
			$this
					.on("expand",
							function() {
								var $this = $(this);
								if (typeof $this.treegrid("getSetting",
										"onExpand") === "function") {
									$this.treegrid("getSetting", "onExpand")
											.apply($this)
								}
							});
			return $this
		},
		initExpander : function() {
			var $this = $(this);
			var cell = $this.find("td").get(
					$this.treegrid("getSetting", "treeColumn"));
			var tpl = $this.treegrid("getSetting", "expanderTemplate");
			var expander = $this.treegrid("getSetting", "getExpander").apply(
					this);
			if (expander) {
				expander.remove()
			}
			$(tpl).prependTo(cell).click(function() {
				$($(this).closest("tr")).treegrid("toggle")
			});
			return $this
		},
		initIndent : function() {
			var $this = $(this);
			$this.find(".treegrid-indent").remove();
			for (var i = 0; i < $(this).treegrid("getDepth"); i++) {
				$($this.treegrid("getSetting", "indentTemplate")).insertBefore(
						$this.find(".treegrid-expander"))
			}
			return $this
		},
		initState : function() {
			var $this = $(this);
			if ($this.treegrid("getSetting", "saveState")
					&& !$this.treegrid("isFirstInit")) {
				$this.treegrid("restoreState")
			} else {
				if ($this.treegrid("getSetting", "initialState") === "expanded") {
					$this.treegrid("expand")
				} else {
					$this.treegrid("collapse")
				}
			}
			return $this
		},
		isFirstInit : function() {
			var tree = $(this).treegrid("getTreeContainer");
			if (tree.data("first_init") === undefined) {
				tree.data("first_init", $.cookie(tree.treegrid("getSetting",
						"saveStateName")) === undefined)
			}
			return tree.data("first_init")
		},
		saveState : function() {
			var $this = $(this);
			if ($this.treegrid("getSetting", "saveStateMethod") === "cookie") {
				var stateArrayString = $.cookie($this.treegrid("getSetting",
						"saveStateName"))
						|| "";
				var stateArray = stateArrayString === "" ? []
						: stateArrayString.split(",");
				var nodeId = $this.treegrid("getNodeId");
				if ($this.treegrid("isExpanded")) {
					if ($.inArray(nodeId, stateArray) === -1) {
						stateArray.push(nodeId)
					}
				} else if ($this.treegrid("isCollapsed")) {
					if ($.inArray(nodeId, stateArray) !== -1) {
						stateArray.splice($.inArray(nodeId, stateArray), 1)
					}
				}
				$.cookie($this.treegrid("getSetting", "saveStateName"),
						stateArray.join(","))
			}
			return $this
		},
		restoreState : function() {
			var $this = $(this);
			if ($this.treegrid("getSetting", "saveStateMethod") === "cookie") {
				var stateArray = $.cookie(
						$this.treegrid("getSetting", "saveStateName")).split(
						",");
				if ($.inArray($this.treegrid("getNodeId"), stateArray) !== -1) {
					$this.treegrid("expand")
				} else {
					$this.treegrid("collapse")
				}
			}
			return $this
		},
		getSetting : function(name) {
			if (!$(this).treegrid("getTreeContainer")) {
				return null
			}
			return $(this).treegrid("getTreeContainer").data("settings")[name]
		},
		setSettings : function(settings) {
			$(this).treegrid("getTreeContainer").data("settings", settings)
		},
		getTreeContainer : function() {
			return $(this).data("treegrid")
		},
		setTreeContainer : function(container) {
			return $(this).data("treegrid", container)
		},
		getRootNodes : function() {
			return $(this).treegrid("getSetting", "getRootNodes").apply(this,
					[ $(this).treegrid("getTreeContainer") ])
		},
		getAllNodes : function() {
			return $(this).treegrid("getSetting", "getAllNodes").apply(this,
					[ $(this).treegrid("getTreeContainer") ])
		},
		isNode : function() {
			return $(this).treegrid("getNodeId") !== null
		},
		getNodeId : function() {
			if ($(this).treegrid("getSetting", "getNodeId") === null) {
				return null
			} else {
				return $(this).treegrid("getSetting", "getNodeId").apply(this)
			}
		},
		getParentNodeId : function() {
			return $(this).treegrid("getSetting", "getParentNodeId")
					.apply(this)
		},
		getParentNode : function() {
			if ($(this).treegrid("getParentNodeId") === null) {
				return null
			} else {
				return $(this).treegrid("getSetting", "getNodeById").apply(
						this,
						[ $(this).treegrid("getParentNodeId"),
								$(this).treegrid("getTreeContainer") ])
			}
		},
		getChildNodes : function() {
			return $(this).treegrid("getSetting", "getChildNodes").apply(
					this,
					[ $(this).treegrid("getNodeId"),
							$(this).treegrid("getTreeContainer") ])
		},
		getDepth : function() {
			if ($(this).treegrid("getParentNode") === null) {
				return 0
			}
			return $(this).treegrid("getParentNode").treegrid("getDepth") + 1
		},
		isRoot : function() {
			return $(this).treegrid("getDepth") === 0
		},
		isLeaf : function() {
			return $(this).treegrid("getChildNodes").length === 0
		},
		isLast : function() {
			if ($(this).treegrid("isNode")) {
				var parentNode = $(this).treegrid("getParentNode");
				if (parentNode === null) {
					if ($(this).treegrid("getNodeId") === $(this).treegrid(
							"getRootNodes").last().treegrid("getNodeId")) {
						return true
					}
				} else {
					if ($(this).treegrid("getNodeId") === parentNode.treegrid(
							"getChildNodes").last().treegrid("getNodeId")) {
						return true
					}
				}
			}
			return false
		},
		isFirst : function() {
			if ($(this).treegrid("isNode")) {
				var parentNode = $(this).treegrid("getParentNode");
				if (parentNode === null) {
					if ($(this).treegrid("getNodeId") === $(this).treegrid(
							"getRootNodes").first().treegrid("getNodeId")) {
						return true
					}
				} else {
					if ($(this).treegrid("getNodeId") === parentNode.treegrid(
							"getChildNodes").first().treegrid("getNodeId")) {
						return true
					}
				}
			}
			return false
		},
		isExpanded : function() {
			return $(this).hasClass("treegrid-expanded")
		},
		isCollapsed : function() {
			return $(this).hasClass("treegrid-collapsed")
		},
		isOneOfParentsCollapsed : function() {
			var $this = $(this);
			if ($this.treegrid("isRoot")) {
				return false
			} else {
				if ($this.treegrid("getParentNode").treegrid("isCollapsed")) {
					return true
				} else {
					return $this.treegrid("getParentNode").treegrid(
							"isOneOfParentsCollapsed")
				}
			}
		},
		expand : function() {
			return $(this).each(
					function() {
						var $this = $(this);
						if (!$this.treegrid("isLeaf")
								&& !$this.treegrid("isExpanded")) {
							$this.trigger("expand");
							$this.trigger("change")
						}
					})
		},
		expandAll : function() {
			var $this = $(this);
			$this.treegrid("getRootNodes").treegrid("expandRecursive");
			return $this
		},
		expandRecursive : function() {
			return $(this).each(function() {
				var $this = $(this);
				$this.treegrid("expand");
				if (!$this.treegrid("isLeaf")) {
					$this.treegrid("getChildNodes").treegrid("expandRecursive")
				}
			})
		},
		collapse : function() {
			return $(this).each(
					function() {
						var $this = $(this);
						if (!$this.treegrid("isLeaf")
								&& !$this.treegrid("isCollapsed")) {
							$this.trigger("collapse");
							$this.trigger("change")
						}
					})
		},
		collapseAll : function() {
			var $this = $(this);
			$this.treegrid("getRootNodes").treegrid("collapseRecursive");
			return $this
		},
		collapseRecursive : function() {
			return $(this).each(
					function() {
						var $this = $(this);
						$this.treegrid("collapse");
						if (!$this.treegrid("isLeaf")) {
							$this.treegrid("getChildNodes").treegrid(
									"collapseRecursive")
						}
					})
		},
		toggle : function() {
			var $this = $(this);
			if ($this.treegrid("isExpanded")) {
				$this.treegrid("collapse")
			} else {
				$this.treegrid("expand")
			}
			return $this
		},
		render : function() {
			return $(this).each(function() {
				var $this = $(this);
				if ($this.treegrid("isOneOfParentsCollapsed")) {
					$this.hide()
				} else {
					$this.show()
				}
				if (!$this.treegrid("isLeaf")) {
					$this.treegrid("renderExpander");
					$this.treegrid("getChildNodes").treegrid("render")
				}
			})
		},
		renderExpander : function() {
			return $(this)
					.each(
							function() {
								var $this = $(this);
								var expander = $this.treegrid("getSetting",
										"getExpander").apply(this);
								if (expander) {
									if (!$this.treegrid("isCollapsed")) {
										expander.removeClass($this.treegrid(
												"getSetting",
												"expanderCollapsedClass"));
										expander.addClass($this.treegrid(
												"getSetting",
												"expanderExpandedClass"))
									} else {
										expander.removeClass($this.treegrid(
												"getSetting",
												"expanderExpandedClass"));
										expander.addClass($this.treegrid(
												"getSetting",
												"expanderCollapsedClass"))
									}
								} else {
									$this.treegrid("initExpander");
									$this.treegrid("renderExpander")
								}
							})
		}
	};
	$.fn.treegrid = function(method) {
		if (methods[method]) {
			return methods[method].apply(this, Array.prototype.slice.call(
					arguments, 1))
		} else if (typeof method === "object" || !method) {
			return methods.initTree.apply(this, arguments)
		} else {
			$.error("Method with name " + method
					+ " does not exists for jQuery.treegrid")
		}
	};
	$.fn.treegrid.defaults = {
		initialState : "expanded",
		saveState : false,
		saveStateMethod : "cookie",
		saveStateName : "tree-grid-state",
		expanderTemplate : '<span class="treegrid-expander"></span>',
		indentTemplate : '<span class="treegrid-indent"></span>',
		expanderExpandedClass : "treegrid-expander-expanded",
		expanderCollapsedClass : "treegrid-expander-collapsed",
		treeColumn : 0,
		getExpander : function() {
			return $(this).find(".treegrid-expander")
		},
		getNodeId : function() {
			var template = /treegrid-([A-Za-z0-9_-]+)/;
			if (template.test($(this).attr("class"))) {
				return template.exec($(this).attr("class"))[1]
			}
			return null
		},
		getParentNodeId : function() {
			var template = /treegrid-parent-([A-Za-z0-9_-]+)/;
			if (template.test($(this).attr("class"))) {
				return template.exec($(this).attr("class"))[1]
			}
			return null
		},
		getNodeById : function(id, treegridContainer) {
			var templateClass = "treegrid-" + id;
			return treegridContainer.find("tr." + templateClass)
		},
		getChildNodes : function(id, treegridContainer) {
			var templateClass = "treegrid-parent-" + id;
			return treegridContainer.find("tr." + templateClass)
		},
		getTreeGridContainer : function() {
			return $(this).closest("table")
		},
		getRootNodes : function(treegridContainer) {
			var result = $
					.grep(
							treegridContainer.find("tr"),
							function(element) {
								var classNames = $(element).attr("class");
								var templateClass = /treegrid-([A-Za-z0-9_-]+)/;
								var templateParentClass = /treegrid-parent-([A-Za-z0-9_-]+)/;
								return templateClass.test(classNames)
										&& !templateParentClass
												.test(classNames)
							});
			return $(result)
		},
		getAllNodes : function(treegridContainer) {
			var result = $.grep(treegridContainer.find("tr"),
					function(element) {
						var classNames = $(element).attr("class");
						var templateClass = /treegrid-([A-Za-z0-9_-]+)/;
						return templateClass.test(classNames)
					});
			return $(result)
		},
		onCollapse : null,
		onExpand : null,
		onChange : null
	}
})(jQuery);